/**
 * 集群路由器，处理集群内的消息路由
 */
import { ActorSystem } from '../actor/actorSystem';
import { PID } from '../actor/pid';
import { ProcessRegistry } from '../actor/processRegistry';
import { Cluster } from './cluster';
import { Libp2pProvider } from './providers/libp2pProvider';

/**
 * 集群路由器负责处理集群内的消息路由
 * 它处理不同节点间 Actor 的通信
 */
export class ClusterRouter {
  private actorSystem: ActorSystem;
  private cluster: Cluster;
  private registeredMembers: Map<string, string> = new Map(); // memberId -> address
  
  constructor(actorSystem: ActorSystem, cluster: Cluster) {
    this.actorSystem = actorSystem;
    this.cluster = cluster;
    
    // 注册到处理注册表
    this.registerWithProcessRegistry();
    
    // 订阅集群事件
    this.subscribeToClusterEvents();
    
    console.log(`[CLUSTER_ROUTER] 初始化完成`);
  }
  
  /**
   * 注册到 ProcessRegistry
   */
  private registerWithProcessRegistry(): void {
    // 获取进程注册表
    const registry = ProcessRegistry.getInstance();
    
    // 扩展 ProcessRegistry 的 get 方法，添加集群 PID 处理
    const originalGet = registry.get;
    
    if (originalGet && typeof originalGet === 'function') {
      // @ts-ignore - 扩展现有方法
      registry.get = (pid: PID) => {
        // 先尝试本地查找
        const localResult = originalGet.call(registry, pid);
        if (localResult) return localResult;
        
        // 如果本地找不到，检查是否为集群 PID
        if (this.isClusterPid(pid)) {
          console.log(`[CLUSTER_ROUTER] 处理集群 PID: ${pid.toString()}`);
          return this.createClusterProcess(pid);
        }
        
        return undefined;
      };
      
      console.log(`[CLUSTER_ROUTER] 已扩展 ProcessRegistry.get 方法`);
    }
  }
  
  /**
   * 判断是否为集群 PID
   */
  private isClusterPid(pid: PID): boolean {
    // 检查是否为已知的集群成员地址
    return Array.from(this.registeredMembers.values()).some(address => 
      pid.address === address);
  }
  
  /**
   * 为集群 PID 创建处理流程
   */
  private createClusterProcess(pid: PID): any {
    console.log(`[CLUSTER_ROUTER] 为 ${pid.toString()} 创建集群处理流程`);
    
    // 返回一个实现了 Process 接口的对象，并添加额外的路由方法
    const process = {
      // Process 接口标准方法
      sendUserMessage: (targetPid: PID, message: any) => {
        console.log(`[CLUSTER_ROUTER] 发送用户消息到 ${targetPid.toString()}`);
        this.routeMessage(targetPid, message, false);
      },
      
      sendSystemMessage: (targetPid: PID, message: any) => {
        console.log(`[CLUSTER_ROUTER] 发送系统消息到 ${targetPid.toString()}`);
        this.routeMessage(targetPid, message, true);
      },
      
      stop: (targetPid: PID) => {
        console.log(`[CLUSTER_ROUTER] 停止进程 ${targetPid.toString()}`);
        this.routeMessage(targetPid, { type: 'Stop' }, true);
      },
      
      // 兼容扩展方法
      send: (targetPid: PID, message: any) => {
        console.log(`[CLUSTER_ROUTER] 通用发送到 ${targetPid.toString()}`);
        this.routeMessage(targetPid, message, false);
      },
      
      routeMessage: (targetPid: PID, message: any, isSystem: boolean) => {
        console.log(`[CLUSTER_ROUTER] 直接路由消息到 ${targetPid.toString()}, isSystem=${isSystem}`);
        this.routeMessage(targetPid, message, isSystem);
      }
    };
    
    console.log(`[CLUSTER_ROUTER] 创建了进程对象，方法有: ${Object.keys(process).join(', ')}`);
    return process;
  }
  
  /**
   * 路由消息到目标节点
   */
  private routeMessage(target: PID, message: any, isSystem: boolean): void {
    try {
      // 获取目标节点地址
      const address = target.address;
      console.log(`[CLUSTER_ROUTER] 路由消息到地址: ${address}`);
      
      // 获取 libp2p 提供者
      const provider = this.cluster.getProvider() as Libp2pProvider;
      if (!provider) {
        throw new Error('No cluster provider available');
      }
      
      // 处理消息信封 (如果消息已经是信封格式)
      let finalMessage = message;
      if (message && message.message !== undefined && message.sender !== undefined) {
        console.log(`[CLUSTER_ROUTER] 检测到信封格式消息，目标: ${target.toString()}`);
        finalMessage = message;
      }
      
      // 构造路由消息
      const routeMessage = {
        type: isSystem ? 'system' : 'user',
        target: {
          address: target.address,
          id: target.id
        },
        message: finalMessage,
        sender: finalMessage.sender ? {
          address: finalMessage.sender.address,
          id: finalMessage.sender.id
        } : null
      };
      
      // 发送到目标节点
      console.log(`[CLUSTER_ROUTER] 通过 libp2p 发送消息到 ${address}`);
      provider.sendDirectMessage(address, 'actor.route', routeMessage);
      
    } catch (error) {
      console.error(`[CLUSTER_ROUTER] 路由消息失败:`, error);
    }
  }
  
  /**
   * 订阅集群事件
   */
  private subscribeToClusterEvents(): void {
    // 添加成员信息
    this.cluster.on('memberJoined', (member: { id: string; address: string }) => {
      console.log(`[CLUSTER_ROUTER] 成员加入: ${member.id}, 地址: ${member.address}`);
      this.registeredMembers.set(member.id, member.address);
    });
    
    // 移除离开的成员
    this.cluster.on('memberLeft', (member: { id: string }) => {
      console.log(`[CLUSTER_ROUTER] 成员离开: ${member.id}`);
      this.registeredMembers.delete(member.id);
    });
  }
  
  /**
   * 处理入站消息
   * 接收从其他节点发来的消息并分发给本地Actor
   */
  public handleInboundMessage(message: any): void {
    try {
      console.log(`[CLUSTER_ROUTER] 处理入站消息:`, message);
      
      // 验证消息格式
      if (!message || !message.target || !message.type) {
        console.warn(`[CLUSTER_ROUTER] 无效的入站消息格式`);
        return;
      }
      
      // 构造目标 PID
      const targetPid = new PID(
        message.target.address,
        message.target.id
      );
      
      // 获取本地地址
      const localAddress = ProcessRegistry.getInstance().address();
      
      // 验证消息是否发送到本地
      if (targetPid.address !== localAddress) {
        console.warn(`[CLUSTER_ROUTER] 消息目标不是本地节点: ${targetPid.address} vs ${localAddress}`);
        return;
      }
      
      // 获取目标进程
      const registry = ProcessRegistry.getInstance();
      const process = registry.get(targetPid);
      
      if (!process) {
        console.warn(`[CLUSTER_ROUTER] 未找到目标进程: ${targetPid.toString()}`);
        return;
      }
      
      // 处理消息内容和信封
      let finalMessage = message.message;
      
      // 构建发送者 PID (如果存在)
      let senderPid = null;
      if (message.sender) {
        console.log(`[CLUSTER_ROUTER] 消息包含发送者信息: ${message.sender.address}/${message.sender.id}`);
        senderPid = new PID(message.sender.address, message.sender.id);
        senderPid.system = this.actorSystem;
      }
      
      // 创建信封 (如果需要)
      if (senderPid) {
        finalMessage = {
          message: finalMessage,
          sender: senderPid
        };
      }
      
      // 根据消息类型发送
      if (message.type === 'system') {
        console.log(`[CLUSTER_ROUTER] 发送系统消息到本地进程: ${targetPid.toString()}`);
        process.sendSystemMessage(targetPid, finalMessage);
      } else {
        console.log(`[CLUSTER_ROUTER] 发送用户消息到本地进程: ${targetPid.toString()}`);
        process.sendUserMessage(targetPid, finalMessage);
      }
      
    } catch (error) {
      console.error(`[CLUSTER_ROUTER] 处理入站消息失败:`, error);
    }
  }
} 